blob: b28a51128a2d8e8351be57a4fb31f917fb29c100 [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
28#include <openbsc/osmo_msc.h>
29#include <openbsc/vlr.h>
30#include <openbsc/debug.h>
31#include <openbsc/transaction.h>
Harald Welte2483f1b2016-06-19 18:06:02 +020032#include <openbsc/signal.h>
33
34#define SUBSCR_CONN_TIMEOUT 5 /* seconds */
Harald Welteb8b85a12016-06-17 00:06:42 +020035
36static const struct value_string subscr_conn_fsm_event_names[] = {
37 OSMO_VALUE_STRING(SUBSCR_CONN_E_INVALID),
Harald Welte2483f1b2016-06-19 18:06:02 +020038 OSMO_VALUE_STRING(SUBSCR_CONN_E_START),
Harald Welteb8b85a12016-06-17 00:06:42 +020039 OSMO_VALUE_STRING(SUBSCR_CONN_E_ACCEPTED),
Harald Welte2483f1b2016-06-19 18:06:02 +020040 OSMO_VALUE_STRING(SUBSCR_CONN_E_COMMUNICATING),
Harald Welteb8b85a12016-06-17 00:06:42 +020041 OSMO_VALUE_STRING(SUBSCR_CONN_E_BUMP),
42 OSMO_VALUE_STRING(SUBSCR_CONN_E_MO_CLOSE),
43 OSMO_VALUE_STRING(SUBSCR_CONN_E_CN_CLOSE),
Harald Welteb8b85a12016-06-17 00:06:42 +020044 { 0, NULL }
45};
46
47const struct value_string subscr_conn_from_names[] = {
48 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_INVALID),
49 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_LU),
50 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_CM_SERVICE_REQ),
51 OSMO_VALUE_STRING(SUBSCR_CONN_FROM_PAGING_RESP),
52 { 0, NULL }
53};
54
55static void paging_resp(struct gsm_subscriber_connection *conn,
56 enum gsm_paging_event pe)
57{
Harald Welte2483f1b2016-06-19 18:06:02 +020058 subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->vsub);
59}
60
61void subscr_conn_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
62{
63 OSMO_ASSERT(event == SUBSCR_CONN_E_START);
64 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_NEW,
65 SUBSCR_CONN_TIMEOUT, 0);
Harald Welteb8b85a12016-06-17 00:06:42 +020066}
67
68void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data)
69{
70 struct gsm_subscriber_connection *conn = fi->priv;
71 enum subscr_conn_from from = SUBSCR_CONN_FROM_INVALID;
Harald Welte2483f1b2016-06-19 18:06:02 +020072 bool success;
Harald Welteb8b85a12016-06-17 00:06:42 +020073
74 if (data) {
75 from = *(enum subscr_conn_from*)data;
76 LOGPFSM(fi, "%s\n", subscr_conn_from_name(from));
77 }
78
79 /* If accepted, transition the state, all other cases mean failure. */
80 switch (event) {
81 case SUBSCR_CONN_E_ACCEPTED:
Harald Welte2483f1b2016-06-19 18:06:02 +020082 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED,
83 SUBSCR_CONN_TIMEOUT, 0);
Harald Welteb8b85a12016-06-17 00:06:42 +020084 break;
85
86 case SUBSCR_CONN_E_MO_CLOSE:
87 case SUBSCR_CONN_E_CN_CLOSE:
Harald Welteb8b85a12016-06-17 00:06:42 +020088 break;
89
90 default:
91 LOGPFSM(fi, "Unexpected event: %d %s\n",
92 event, osmo_fsm_event_name(fi->fsm, event));
93 break;
94 }
95
Harald Welte2483f1b2016-06-19 18:06:02 +020096 success = (fi->state == SUBSCR_CONN_S_ACCEPTED);
97
98 if (from == SUBSCR_CONN_FROM_LU)
99 rate_ctr_inc(&conn->network->msc_ctrs->ctr[
100 success ? MSC_CTR_LOC_UPDATE_COMPLETED
101 : MSC_CTR_LOC_UPDATE_FAILED]);
102
103 /* signal paging success or failure in case this was a paging */
104 if (from == SUBSCR_CONN_FROM_PAGING_RESP)
105 paging_resp(conn,
106 success ? GSM_PAGING_SUCCEEDED
107 : GSM_PAGING_EXPIRED);
Harald Welteb8b85a12016-06-17 00:06:42 +0200108
109 /* On failure, discard the conn */
Harald Welte2483f1b2016-06-19 18:06:02 +0200110 if (!success) {
Harald Welteb8b85a12016-06-17 00:06:42 +0200111 /* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and
112 * await BSC confirmation? */
113 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
114 return;
115 }
116
Harald Welteb8b85a12016-06-17 00:06:42 +0200117 if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) {
118 conn->received_cm_service_request = true;
119 LOGPFSM(fi, "received_cm_service_request = true\n");
120 }
121
122 osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data);
123}
124
Harald Welteb8b85a12016-06-17 00:06:42 +0200125static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data)
126{
127 struct gsm_subscriber_connection *conn = fi->priv;
128
129 if (conn->silent_call)
130 return;
131
132 if (conn->received_cm_service_request)
133 return;
134
Harald Welte2483f1b2016-06-19 18:06:02 +0200135 if (conn->vsub && !llist_empty(&conn->vsub->cs.requests))
Harald Welteb8b85a12016-06-17 00:06:42 +0200136 return;
137
138 if (trans_has_conn(conn))
139 return;
140
141 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
142}
143
Harald Welte2483f1b2016-06-19 18:06:02 +0200144static void subscr_conn_fsm_accepted_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
145{
146 struct gsm_subscriber_connection *conn = fi->priv;
147 osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, conn->vsub);
148}
149
Harald Welteb8b85a12016-06-17 00:06:42 +0200150static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, void *data)
151{
152 switch (event) {
Harald Welte2483f1b2016-06-19 18:06:02 +0200153 case SUBSCR_CONN_E_COMMUNICATING:
154 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_COMMUNICATING, 0, 0);
155 return;
156
Harald Welteb8b85a12016-06-17 00:06:42 +0200157 case SUBSCR_CONN_E_BUMP:
158 subscr_conn_fsm_bump(fi, event, data);
159 return;
160
161 default:
162 break;
163 }
164 /* Whatever unexpected happens in the accepted state, it means release.
165 * Even if an unexpected event is passed, the safest thing to do is
166 * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
167 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
168}
169
Harald Welte2483f1b2016-06-19 18:06:02 +0200170static void subscr_conn_fsm_communicating(struct osmo_fsm_inst *fi, uint32_t event, void *data)
171{
172 switch (event) {
173 case SUBSCR_CONN_E_COMMUNICATING:
174 /* no-op */
175 return;
176
177 case SUBSCR_CONN_E_BUMP:
178 subscr_conn_fsm_bump(fi, event, data);
179 return;
180
181 default:
182 break;
183 }
184 /* Whatever unexpected happens in the accepted state, it means release.
185 * Even if an unexpected event is passed, the safest thing to do is
186 * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */
187 osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0);
188}
189
190static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi,
191 enum osmo_fsm_term_cause cause)
Harald Welteb8b85a12016-06-17 00:06:42 +0200192{
193 struct gsm_subscriber_connection *conn = fi->priv;
Harald Welte2483f1b2016-06-19 18:06:02 +0200194 fi->priv = NULL;
195
Harald Welteb8b85a12016-06-17 00:06:42 +0200196 if (!conn)
197 return;
198
Harald Welte2483f1b2016-06-19 18:06:02 +0200199 conn->conn_fsm = NULL;
200 msc_subscr_conn_close(conn, cause);
201 msc_subscr_conn_put(conn);
202}
Harald Welteb8b85a12016-06-17 00:06:42 +0200203
Harald Welte2483f1b2016-06-19 18:06:02 +0200204int subscr_conn_fsm_timeout(struct osmo_fsm_inst *fi)
205{
206 struct gsm_subscriber_connection *conn = fi->priv;
207 if (conn)
208 vlr_subscr_conn_timeout(conn->vsub);
209 osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_CN_CLOSE, NULL);
210 return 0;
211}
212
213static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state)
214{
215 osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
Harald Welteb8b85a12016-06-17 00:06:42 +0200216}
217
218#define S(x) (1 << (x))
219
220static const struct osmo_fsm_state subscr_conn_fsm_states[] = {
Harald Welte2483f1b2016-06-19 18:06:02 +0200221 [SUBSCR_CONN_S_INIT] = {
222 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_INIT),
223 .in_event_mask = S(SUBSCR_CONN_E_START),
224 .out_state_mask = S(SUBSCR_CONN_S_NEW),
225 .action = subscr_conn_fsm_init,
226 },
Harald Welteb8b85a12016-06-17 00:06:42 +0200227 [SUBSCR_CONN_S_NEW] = {
228 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_NEW),
229 .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) |
230 S(SUBSCR_CONN_E_MO_CLOSE) |
Harald Welte2483f1b2016-06-19 18:06:02 +0200231 S(SUBSCR_CONN_E_CN_CLOSE),
Harald Welteb8b85a12016-06-17 00:06:42 +0200232 .out_state_mask = S(SUBSCR_CONN_S_ACCEPTED) |
233 S(SUBSCR_CONN_S_RELEASED),
234 .action = subscr_conn_fsm_new,
235 },
236 [SUBSCR_CONN_S_ACCEPTED] = {
237 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_ACCEPTED),
238 /* allow everything to release for any odd behavior */
Harald Welte2483f1b2016-06-19 18:06:02 +0200239 .in_event_mask = S(SUBSCR_CONN_E_COMMUNICATING) |
240 S(SUBSCR_CONN_E_BUMP) |
241 S(SUBSCR_CONN_E_ACCEPTED) |
Harald Welteb8b85a12016-06-17 00:06:42 +0200242 S(SUBSCR_CONN_E_MO_CLOSE) |
Harald Welte2483f1b2016-06-19 18:06:02 +0200243 S(SUBSCR_CONN_E_CN_CLOSE),
244 .out_state_mask = S(SUBSCR_CONN_S_RELEASED) |
245 S(SUBSCR_CONN_S_COMMUNICATING),
246 .onenter = subscr_conn_fsm_accepted_enter,
Harald Welteb8b85a12016-06-17 00:06:42 +0200247 .action = subscr_conn_fsm_accepted,
248 },
Harald Welte2483f1b2016-06-19 18:06:02 +0200249 [SUBSCR_CONN_S_COMMUNICATING] = {
250 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_COMMUNICATING),
251 /* allow everything to release for any odd behavior */
252 .in_event_mask = S(SUBSCR_CONN_E_BUMP) |
253 S(SUBSCR_CONN_E_ACCEPTED) |
254 S(SUBSCR_CONN_E_COMMUNICATING) |
255 S(SUBSCR_CONN_E_MO_CLOSE) |
256 S(SUBSCR_CONN_E_CN_CLOSE),
257 .out_state_mask = S(SUBSCR_CONN_S_RELEASED),
258 .action = subscr_conn_fsm_communicating,
259 },
Harald Welteb8b85a12016-06-17 00:06:42 +0200260 [SUBSCR_CONN_S_RELEASED] = {
261 .name = OSMO_STRINGIFY(SUBSCR_CONN_S_RELEASED),
262 .onenter = subscr_conn_fsm_release,
263 },
264};
265
266static struct osmo_fsm subscr_conn_fsm = {
267 .name = "Subscr_Conn",
268 .states = subscr_conn_fsm_states,
269 .num_states = ARRAY_SIZE(subscr_conn_fsm_states),
270 .allstate_event_mask = 0,
271 .allstate_action = NULL,
272 .log_subsys = DVLR,
273 .event_names = subscr_conn_fsm_event_names,
Harald Welte2483f1b2016-06-19 18:06:02 +0200274 .cleanup = subscr_conn_fsm_cleanup,
275 .timer_cb = subscr_conn_fsm_timeout,
Harald Welteb8b85a12016-06-17 00:06:42 +0200276};
277
278int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id)
279{
280 struct osmo_fsm_inst *fi;
281 OSMO_ASSERT(conn);
282
283 if (conn->conn_fsm) {
284 LOGP(DMM, LOGL_ERROR,
285 "%s: Error: connection already in use\n", id);
286 return -EINVAL;
287 }
288
Harald Welte2483f1b2016-06-19 18:06:02 +0200289 /* Allocate the FSM not with the subscr_conn. Semantically it would
290 * make sense, but in subscr_conn_fsm_cleanup(), we want to discard the
291 * subscriber connection. If the FSM is freed along with the subscriber
292 * connection, then in _osmo_fsm_inst_term() the osmo_fsm_inst_free()
293 * that follows the cleanup() call would run into a double free. */
294 fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn->network,
295 msc_subscr_conn_get(conn),
296 LOGL_DEBUG, id);
Harald Welteb8b85a12016-06-17 00:06:42 +0200297
298 if (!fi) {
299 LOGP(DMM, LOGL_ERROR,
300 "%s: Failed to allocate subscr conn master FSM\n", id);
301 return -ENOMEM;
302 }
303 conn->conn_fsm = fi;
Harald Welte2483f1b2016-06-19 18:06:02 +0200304 osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_START, NULL);
Harald Welteb8b85a12016-06-17 00:06:42 +0200305 return 0;
306}
307
308bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn)
309{
310 if (!conn)
311 return false;
Harald Welte2483f1b2016-06-19 18:06:02 +0200312 if (!conn->vsub)
Harald Welteb8b85a12016-06-17 00:06:42 +0200313 return false;
314 if (!conn->conn_fsm)
315 return false;
Harald Welte2483f1b2016-06-19 18:06:02 +0200316 if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED
317 || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING))
Harald Welteb8b85a12016-06-17 00:06:42 +0200318 return false;
319 return true;
320}
321
Harald Welte2483f1b2016-06-19 18:06:02 +0200322void msc_subscr_conn_communicating(struct gsm_subscriber_connection *conn)
323{
324 OSMO_ASSERT(conn);
325 osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_COMMUNICATING,
326 NULL);
327}
328
Harald Welteb8b85a12016-06-17 00:06:42 +0200329void msc_subscr_conn_init(void)
330{
331 osmo_fsm_register(&subscr_conn_fsm);
332}