blob: ee9e6b8528538f1770b8dd6aa1777d3de678756e [file] [log] [blame]
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +01001/* Roughly following "Process Update_Location_HLR" of TS 09.02 */
2
3/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
4 *
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include <osmocom/core/utils.h>
23#include <osmocom/core/tdef.h>
24#include <osmocom/core/linuxlist.h>
25#include <osmocom/core/fsm.h>
26#include <osmocom/gsm/apn.h>
27#include <osmocom/gsm/gsm48_ie.h>
28
Neels Hofmeyr008ce4b2019-12-04 01:04:32 +010029#include <osmocom/gsupclient/gsup_peer_id.h>
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +010030#include <osmocom/gsupclient/gsup_req.h>
31#include <osmocom/hlr/logging.h>
32#include <osmocom/hlr/hlr.h>
33#include <osmocom/hlr/gsup_server.h>
34
35#include <osmocom/hlr/db.h>
36
37#define LOG_LU(lu, level, fmt, args...) \
38 LOGPFSML((lu)? (lu)->fi : NULL, level, fmt, ##args)
39
40#define LOG_LU_REQ(lu, req, level, fmt, args...) \
41 LOGPFSML((lu)? (lu)->fi : NULL, level, "%s:" fmt, \
42 osmo_gsup_message_type_name((req)->gsup.message_type), ##args)
43
44struct lu {
45 struct llist_head entry;
46 struct osmo_fsm_inst *fi;
47
48 struct osmo_gsup_req *update_location_req;
49
50 /* Subscriber state at time of initial Update Location Request */
51 struct hlr_subscriber subscr;
52 bool is_ps;
53
54 /* VLR requesting the LU. */
Neels Hofmeyr008ce4b2019-12-04 01:04:32 +010055 struct osmo_gsup_peer_id vlr_name;
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +010056
57 /* If the LU request was received via a proxy and not immediately from a local VLR, this indicates the closest
58 * peer that forwarded the GSUP message. */
Neels Hofmeyr008ce4b2019-12-04 01:04:32 +010059 struct osmo_gsup_peer_id via_proxy;
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +010060};
61LLIST_HEAD(g_all_lu);
62
63enum lu_fsm_event {
64 LU_EV_RX_GSUP,
65};
66
67enum lu_fsm_state {
68 LU_ST_UNVALIDATED,
69 LU_ST_WAIT_INSERT_DATA_RESULT,
70 LU_ST_WAIT_LOCATION_CANCEL_RESULT,
71};
72
73static const struct value_string lu_fsm_event_names[] = {
74 OSMO_VALUE_STRING(LU_EV_RX_GSUP),
75 {}
76};
77
78static struct osmo_tdef_state_timeout lu_fsm_timeouts[32] = {
79 [LU_ST_WAIT_INSERT_DATA_RESULT] = { .T = -4222 },
80 [LU_ST_WAIT_LOCATION_CANCEL_RESULT] = { .T = -4222 },
81};
82
83#define lu_state_chg(lu, state) \
84 osmo_tdef_fsm_inst_state_chg((lu)->fi, state, lu_fsm_timeouts, g_hlr_tdefs, 5)
85
86static void lu_success(struct lu *lu)
87{
88 if (!lu->update_location_req)
89 LOG_LU(lu, LOGL_ERROR, "No request for this LU\n");
90 else
91 osmo_gsup_req_respond_msgt(lu->update_location_req, OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT, true);
92 lu->update_location_req = NULL;
93 osmo_fsm_inst_term(lu->fi, OSMO_FSM_TERM_REGULAR, NULL);
94}
95
96#define lu_failure(LU, CAUSE, log_msg, args...) do { \
97 if (!(LU)->update_location_req) \
98 LOG_LU(LU, LOGL_ERROR, "No request for this LU\n"); \
99 else \
100 osmo_gsup_req_respond_err((LU)->update_location_req, CAUSE, log_msg, ##args); \
101 (LU)->update_location_req = NULL; \
102 osmo_fsm_inst_term((LU)->fi, OSMO_FSM_TERM_REGULAR, NULL); \
103 } while(0)
104
105static struct osmo_fsm lu_fsm;
106
107static void lu_start(struct osmo_gsup_req *update_location_req)
108{
109 struct osmo_fsm_inst *fi;
110 struct lu *lu;
Neels Hofmeyrce172ef2018-12-26 01:49:53 +0100111 bool any_rat_allowed;
112 int i;
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +0100113
114 OSMO_ASSERT(update_location_req);
115 OSMO_ASSERT(update_location_req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST);
116
117 fi = osmo_fsm_inst_alloc(&lu_fsm, g_hlr, NULL, LOGL_DEBUG, update_location_req->gsup.imsi);
118 OSMO_ASSERT(fi);
119
120 lu = talloc(fi, struct lu);
121 OSMO_ASSERT(lu);
122 fi->priv = lu;
123 *lu = (struct lu){
124 .fi = fi,
125 .update_location_req = update_location_req,
126 .vlr_name = update_location_req->source_name,
127 .via_proxy = update_location_req->via_proxy,
128 /* According to GSUP specs, OSMO_GSUP_CN_DOMAIN_PS is the default. */
129 .is_ps = (update_location_req->gsup.cn_domain != OSMO_GSUP_CN_DOMAIN_CS),
130 };
131 llist_add(&lu->entry, &g_all_lu);
132
133 osmo_fsm_inst_update_id_f_sanitize(fi, '_', "%s:IMSI-%s", lu->is_ps ? "PS" : "CS", update_location_req->gsup.imsi);
134
Neels Hofmeyr008ce4b2019-12-04 01:04:32 +0100135 if (osmo_gsup_peer_id_is_empty(&lu->vlr_name)) {
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +0100136 lu_failure(lu, GMM_CAUSE_NET_FAIL, "LU without a VLR");
137 return;
138 }
139
140 if (db_subscr_get_by_imsi(g_hlr->dbc, update_location_req->gsup.imsi, &lu->subscr) < 0) {
gsmevent admind8e8a842019-08-17 21:43:07 +0200141 lu_failure(lu, GMM_CAUSE_ROAMING_NOTALLOWED, "Subscriber does not exist");
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +0100142 return;
143 }
144
145 /* Check if subscriber is generally permitted on CS or PS
146 * service (as requested) */
147 if (!lu->is_ps && !lu->subscr.nam_cs) {
148 lu_failure(lu, GMM_CAUSE_PLMN_NOTALLOWED, "nam_cs == false");
149 return;
150 }
151 if (lu->is_ps && !lu->subscr.nam_ps) {
152 lu_failure(lu, GMM_CAUSE_GPRS_NOTALLOWED, "nam_ps == false");
153 return;
154 }
155
Neels Hofmeyrce172ef2018-12-26 01:49:53 +0100156 /* Check if any available RAT type is allowed. See 3GPP TS 29.010 3.2 'Routeing area updating' and 3.8 'Location
157 * update' for the "No Suitable cells in location area" error code. */
158 any_rat_allowed = false;
159 for (i = 0; i < update_location_req->gsup.supported_rat_types_len; i++) {
160 enum osmo_rat_type rat = update_location_req->gsup.supported_rat_types[i];
161 if (rat <= 0 || rat >= OSMO_RAT_COUNT) {
162 lu_failure(lu, GMM_CAUSE_COND_IE_ERR, "Invalid RAT type in GSUP request: %s",
163 osmo_rat_type_name(rat));
164 return;
165 }
166 if (lu->subscr.rat_types[rat]) {
167 any_rat_allowed = true;
168 LOG_LU(lu, LOGL_DEBUG, "subscriber allowed on %s\n", osmo_rat_type_name(rat));
169 } else {
170 LOG_LU(lu, LOGL_DEBUG, "subscriber not allowed on %s\n", osmo_rat_type_name(rat));
171 }
172 }
173 if (!any_rat_allowed && update_location_req->gsup.supported_rat_types_len > 0) {
174 lu_failure(lu, GMM_CAUSE_NO_SUIT_CELL_IN_LA, "subscriber not allowed on any available RAT type");
175 return;
176 }
177
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +0100178 /* TODO: Set subscriber tracing = deactive in VLR/SGSN */
179
180#if 0
181 /* Cancel in old VLR/SGSN, if new VLR/SGSN differs from old */
182 if (!lu->is_ps && strcmp(subscr->vlr_number, vlr_number)) {
183 lu_op_tx_cancel_old(lu);
184 } else if (lu->is_ps && strcmp(subscr->sgsn_number, sgsn_number)) {
185 lu_op_tx_cancel_old(lu);
186 }
187#endif
188
189 /* Store the VLR / SGSN number with the subscriber, so we know where it was last seen. */
Neels Hofmeyr008ce4b2019-12-04 01:04:32 +0100190 if (!osmo_gsup_peer_id_is_empty(&lu->via_proxy)) {
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +0100191 LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s, via proxy %s\n",
192 lu->is_ps ? "SGSN number" : "VLR number",
Neels Hofmeyr008ce4b2019-12-04 01:04:32 +0100193 osmo_gsup_peer_id_to_str(&lu->vlr_name),
194 osmo_gsup_peer_id_to_str(&lu->via_proxy));
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +0100195 } else {
196 LOG_GSUP_REQ(update_location_req, LOGL_DEBUG, "storing %s = %s\n",
197 lu->is_ps ? "SGSN number" : "VLR number",
Neels Hofmeyr008ce4b2019-12-04 01:04:32 +0100198 osmo_gsup_peer_id_to_str(&lu->vlr_name));
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +0100199 }
200
Neels Hofmeyr008ce4b2019-12-04 01:04:32 +0100201 if (osmo_gsup_peer_id_is_empty(&lu->vlr_name)
202 || (lu->vlr_name.type != OSMO_GSUP_PEER_ID_IPA_NAME)) {
203 lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for vlr_name: %s",
204 osmo_gsup_peer_id_type_name(lu->vlr_name.type));
205 return;
206 }
207 if (!osmo_gsup_peer_id_is_empty(&lu->via_proxy) && (lu->via_proxy.type != OSMO_GSUP_PEER_ID_IPA_NAME)) {
208 lu_failure(lu, GMM_CAUSE_PROTO_ERR_UNSPEC, "Unsupported GSUP peer id type for via_proxy: %s",
209 osmo_gsup_peer_id_type_name(lu->via_proxy.type));
210 return;
211 }
212 if (db_subscr_lu(g_hlr->dbc, lu->subscr.id, &lu->vlr_name.ipa_name, lu->is_ps,
Neels Hofmeyr5bc457e2018-12-29 03:28:38 +0100213 osmo_gsup_peer_id_is_empty(&lu->via_proxy)? NULL : &lu->via_proxy.ipa_name,
214 update_location_req->gsup.supported_rat_types, update_location_req->gsup.supported_rat_types_len)) {
Neels Hofmeyrf13a8bc2019-11-20 02:36:45 +0100215 lu_failure(lu, GMM_CAUSE_NET_FAIL, "Cannot update %s in the database",
216 lu->is_ps ? "SGSN number" : "VLR number");
217 return;
218 }
219
220 /* TODO: Subscriber allowed to roam in PLMN? */
221 /* TODO: Update RoutingInfo */
222 /* TODO: Reset Flag MS Purged (cs/ps) */
223 /* TODO: Control_Tracing_HLR / Control_Tracing_HLR_with_SGSN */
224
225 lu_state_chg(lu, LU_ST_WAIT_INSERT_DATA_RESULT);
226}
227
228void lu_rx_gsup(struct osmo_gsup_req *req)
229{
230 struct lu *lu;
231 if (req->gsup.message_type == OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST)
232 return lu_start(req);
233
234 llist_for_each_entry(lu, &g_all_lu, entry) {
235 if (strcmp(lu->subscr.imsi, req->gsup.imsi))
236 continue;
237 if (osmo_fsm_inst_dispatch(lu->fi, LU_EV_RX_GSUP, req)) {
238 LOG_LU_REQ(lu, req, LOGL_ERROR, "Cannot receive GSUP messages in this state\n");
239 osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE,
240 "LU does not accept GSUP rx");
241 }
242 return;
243 }
244 osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE, "No Location Updating in progress for this IMSI");
245}
246
247static int lu_fsm_timer_cb(struct osmo_fsm_inst *fi)
248{
249 struct lu *lu = fi->priv;
250 lu_failure(lu, GSM_CAUSE_NET_FAIL, "Timeout");
251 return 0;
252}
253
254static void lu_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
255{
256 struct lu *lu = fi->priv;
257 if (lu->update_location_req)
258 osmo_gsup_req_respond_err(lu->update_location_req, GSM_CAUSE_NET_FAIL, "LU aborted");
259 lu->update_location_req = NULL;
260 llist_del(&lu->entry);
261}
262
263static void lu_fsm_wait_insert_data_result_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
264{
265 /* Transmit Insert Data Request to the VLR */
266 struct lu *lu = fi->priv;
267 struct hlr_subscriber *subscr = &lu->subscr;
268 struct osmo_gsup_message gsup;
269 uint8_t msisdn_enc[OSMO_GSUP_MAX_CALLED_PARTY_BCD_LEN];
270 uint8_t apn[APN_MAXLEN];
271
272 if (osmo_gsup_create_insert_subscriber_data_msg(&gsup, subscr->imsi,
273 subscr->msisdn, msisdn_enc, sizeof(msisdn_enc),
274 apn, sizeof(apn),
275 lu->is_ps? OSMO_GSUP_CN_DOMAIN_PS : OSMO_GSUP_CN_DOMAIN_CS)) {
276 lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot encode Insert Subscriber Data message");
277 return;
278 }
279
280 if (osmo_gsup_req_respond(lu->update_location_req, &gsup, false, false))
281 lu_failure(lu, GMM_CAUSE_NET_FAIL, "cannot send %s", osmo_gsup_message_type_name(gsup.message_type));
282}
283
284void lu_fsm_wait_insert_data_result(struct osmo_fsm_inst *fi, uint32_t event, void *data)
285{
286 struct lu *lu = fi->priv;
287 struct osmo_gsup_req *req;
288
289 switch (event) {
290 case LU_EV_RX_GSUP:
291 req = data;
292 break;
293 default:
294 OSMO_ASSERT(false);
295 }
296
297 switch (req->gsup.message_type) {
298 case OSMO_GSUP_MSGT_INSERT_DATA_RESULT:
299 osmo_gsup_req_free(req);
300 lu_success(lu);
301 break;
302
303 case OSMO_GSUP_MSGT_INSERT_DATA_ERROR:
304 lu_failure(lu, GMM_CAUSE_NET_FAIL, "Rx %s", osmo_gsup_message_type_name(req->gsup.message_type));
305 break;
306
307 default:
308 osmo_gsup_req_respond_err(req, GMM_CAUSE_MSGT_INCOMP_P_STATE, "unexpected message type in this state");
309 break;
310 }
311}
312
313#define S(x) (1 << (x))
314
315static const struct osmo_fsm_state lu_fsm_states[] = {
316 [LU_ST_UNVALIDATED] = {
317 .name = "UNVALIDATED",
318 .out_state_mask = 0
319 | S(LU_ST_WAIT_INSERT_DATA_RESULT)
320 ,
321 },
322 [LU_ST_WAIT_INSERT_DATA_RESULT] = {
323 .name = "WAIT_INSERT_DATA_RESULT",
324 .in_event_mask = 0
325 | S(LU_EV_RX_GSUP)
326 ,
327 .onenter = lu_fsm_wait_insert_data_result_onenter,
328 .action = lu_fsm_wait_insert_data_result,
329 },
330};
331
332static struct osmo_fsm lu_fsm = {
333 .name = "lu",
334 .states = lu_fsm_states,
335 .num_states = ARRAY_SIZE(lu_fsm_states),
336 .log_subsys = DLU,
337 .event_names = lu_fsm_event_names,
338 .timer_cb = lu_fsm_timer_cb,
339 .cleanup = lu_fsm_cleanup,
340};
341
342static __attribute__((constructor)) void lu_fsm_init()
343{
344 OSMO_ASSERT(osmo_fsm_register(&lu_fsm) == 0);
345}