blob: 18096edb97bfe8a0f2b1c623c61a5babc26be445 [file] [log] [blame]
Harald Weltee687be52016-05-03 18:49:27 +02001/* (C) 2015 by Harald Welte <laforge@gnumonks.org>
2 *
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
Stefan Sperling5c14c9c2018-12-07 12:30:21 +010020#define _POSIX_C_SOURCE 200809L /* for strptime(3) */
21/* These are needed as well due to the above _POSIX_C_SOURCE definition: */
22#define _DEFAULT_SOURCE /* for struct timezone */
23#define _XOPEN_SOURCE /* for clockid_t */
24
Harald Weltee687be52016-05-03 18:49:27 +020025#include <string.h>
Max00b37152017-02-20 11:09:27 +010026#include <errno.h>
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020027#include <inttypes.h>
Stefan Sperling638ba8c2018-12-04 15:07:29 +010028#include <time.h>
Harald Weltee687be52016-05-03 18:49:27 +020029
30#include <osmocom/core/utils.h>
31#include <osmocom/crypt/auth.h>
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020032#include <osmocom/gsm/gsm23003.h>
Harald Weltee687be52016-05-03 18:49:27 +020033
34#include <sqlite3.h>
35
36#include "logging.h"
Neels Hofmeyr00b1d432017-10-17 01:43:48 +020037#include "hlr.h"
Harald Weltee687be52016-05-03 18:49:27 +020038#include "db.h"
Neels Hofmeyr00b1d432017-10-17 01:43:48 +020039#include "gsup_server.h"
40#include "luop.h"
Harald Weltee687be52016-05-03 18:49:27 +020041
Neels Hofmeyr40aa61c2017-10-09 17:56:04 +020042#define LOGHLR(imsi, level, fmt, args ...) LOGP(DAUC, level, "IMSI='%s': " fmt, imsi, ## args)
Harald Weltee687be52016-05-03 18:49:27 +020043
Neels Hofmeyr16140f72017-10-25 19:17:18 +020044/*! Add new subscriber record to the HLR database.
45 * \param[in,out] dbc database context.
46 * \param[in] imsi ASCII string of IMSI digits, is validated.
Oliver Smithcd2af5e2019-03-06 13:17:39 +010047 * \param[in] flags Bitmask of DB_SUBSCR_FLAG_*.
Neels Hofmeyr16140f72017-10-25 19:17:18 +020048 * \returns 0 on success, -EINVAL on invalid IMSI, -EIO on database error.
49 */
Oliver Smithcd2af5e2019-03-06 13:17:39 +010050int db_subscr_create(struct db_context *dbc, const char *imsi, uint8_t flags)
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020051{
52 sqlite3_stmt *stmt;
53 int rc;
54
55 if (!osmo_imsi_str_valid(imsi)) {
56 LOGP(DAUC, LOGL_ERROR, "Cannot create subscriber: invalid IMSI: '%s'\n",
57 imsi);
58 return -EINVAL;
59 }
60
61 stmt = dbc->stmt[DB_STMT_SUBSCR_CREATE];
62
63 if (!db_bind_text(stmt, "$imsi", imsi))
64 return -EIO;
Oliver Smithcd2af5e2019-03-06 13:17:39 +010065 if (!db_bind_int(stmt, "$nam_cs", (flags & DB_SUBSCR_FLAG_NAM_CS) != 0))
66 return -EIO;
67 if (!db_bind_int(stmt, "$nam_ps", (flags & DB_SUBSCR_FLAG_NAM_PS) != 0))
68 return -EIO;
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020069
70 /* execute the statement */
71 rc = sqlite3_step(stmt);
72 db_remove_reset(stmt);
73 if (rc != SQLITE_DONE) {
74 LOGHLR(imsi, LOGL_ERROR, "Cannot create subscriber: SQL error: (%d) %s\n",
75 rc, sqlite3_errmsg(dbc->db));
76 return -EIO;
77 }
78
79 return 0;
80}
81
Neels Hofmeyr16140f72017-10-25 19:17:18 +020082/*! Completely delete a subscriber record from the HLR database.
83 * Also remove authentication data.
84 * Future todo: also drop from all other database tables, which aren't used yet
85 * at the time of writing this.
86 * \param[in,out] dbc database context.
87 * \param[in] subscr_id ID of the subscriber in the HLR db.
88 * \returns if the subscriber was found and removed, -EIO on database error,
89 * -ENOENT if no such subscriber data exists.
90 */
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020091int db_subscr_delete_by_id(struct db_context *dbc, int64_t subscr_id)
92{
93 int rc;
Neels Hofmeyr1332a172017-10-10 02:25:00 +020094 struct sub_auth_data_str aud;
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +020095 int ret = 0;
96
97 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_DEL_BY_ID];
98
99 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
100 return -EIO;
101
102 /* execute the statement */
103 rc = sqlite3_step(stmt);
104 if (rc != SQLITE_DONE) {
105 LOGP(DAUC, LOGL_ERROR,
Stefan Sperling705b61b2018-12-07 12:44:50 +0100106 "Cannot delete subscriber ID=%" PRId64 ": SQL error: (%d) %s\n",
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200107 subscr_id, rc, sqlite3_errmsg(dbc->db));
108 db_remove_reset(stmt);
109 return -EIO;
110 }
111
112 /* verify execution result */
113 rc = sqlite3_changes(dbc->db);
114 if (!rc) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100115 LOGP(DAUC, LOGL_ERROR, "Cannot delete: no such subscriber: ID=%" PRId64 "\n",
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200116 subscr_id);
117 ret = -ENOENT;
118 } else if (rc != 1) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100119 LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%" PRId64
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200120 ": SQL modified %d rows (expected 1)\n", subscr_id, rc);
121 ret = -EIO;
122 }
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200123 db_remove_reset(stmt);
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200124
125 /* make sure to remove authentication data for this subscriber id, for
126 * both 2G and 3G. */
127
128 aud = (struct sub_auth_data_str){
129 .type = OSMO_AUTH_TYPE_GSM,
130 .algo = OSMO_AUTH_ALG_NONE,
131 };
132 rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
133 if (ret == -ENOENT && !rc)
134 ret = 0;
135
136 aud = (struct sub_auth_data_str){
137 .type = OSMO_AUTH_TYPE_UMTS,
138 .algo = OSMO_AUTH_ALG_NONE,
139 };
140 rc = db_subscr_update_aud_by_id(dbc, subscr_id, &aud);
141 if (ret == -ENOENT && !rc)
142 ret = 0;
143
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200144 return ret;
145}
146
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200147/*! Set a subscriber's MSISDN in the HLR database.
148 * \param[in,out] dbc database context.
Oliver Smith2dc7d962019-01-15 14:14:51 +0100149 * \param[in] imsi ASCII string of IMSI digits
150 * \param[in] msisdn ASCII string of MSISDN digits, or NULL to remove the MSISDN.
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200151 * \returns 0 on success, -EINVAL in case of invalid MSISDN string, -EIO on
152 * database failure, -ENOENT if no such subscriber exists.
153 */
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200154int db_subscr_update_msisdn_by_imsi(struct db_context *dbc, const char *imsi,
155 const char *msisdn)
156{
157 int rc;
158 int ret = 0;
159
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100160 if (msisdn && !osmo_msisdn_str_valid(msisdn)) {
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200161 LOGHLR(imsi, LOGL_ERROR,
162 "Cannot update subscriber: invalid MSISDN: '%s'\n",
163 msisdn);
164 return -EINVAL;
165 }
166
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100167 sqlite3_stmt *stmt = dbc->stmt[
168 msisdn ? DB_STMT_SET_MSISDN_BY_IMSI : DB_STMT_DELETE_MSISDN_BY_IMSI];
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200169
170 if (!db_bind_text(stmt, "$imsi", imsi))
171 return -EIO;
Neels Hofmeyra820ea12018-12-02 19:46:46 +0100172 if (msisdn) {
173 if (!db_bind_text(stmt, "$msisdn", msisdn))
174 return -EIO;
175 }
Neels Hofmeyrf7c3e6e2017-10-09 17:55:16 +0200176
177 /* execute the statement */
178 rc = sqlite3_step(stmt);
179 if (rc != SQLITE_DONE) {
180 LOGHLR(imsi, LOGL_ERROR,
181 "Cannot update subscriber's MSISDN: SQL error: (%d) %s\n",
182 rc, sqlite3_errmsg(dbc->db));
183 ret = -EIO;
184 goto out;
185 }
186
187 /* verify execution result */
188 rc = sqlite3_changes(dbc->db);
189 if (!rc) {
190 LOGP(DAUC, LOGL_ERROR, "Cannot update MSISDN: no such subscriber: IMSI='%s'\n",
191 imsi);
192 ret = -ENOENT;
193 goto out;
194 } else if (rc != 1) {
195 LOGHLR(imsi, LOGL_ERROR, "Update MSISDN: SQL modified %d rows (expected 1)\n", rc);
196 ret = -EIO;
197 }
198
199out:
200 db_remove_reset(stmt);
201 return ret;
202
203}
204
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200205/*! Insert or update 2G or 3G authentication tokens in the database.
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200206 * If aud->type is OSMO_AUTH_TYPE_GSM, the auc_2g table entry for the
207 * subscriber will be added or modified; if aud->algo is OSMO_AUTH_ALG_NONE,
208 * however, the auc_2g entry for the subscriber is deleted. If aud->type is
209 * OSMO_AUTH_TYPE_UMTS, the auc_3g table is updated; again, if aud->algo is
210 * OSMO_AUTH_ALG_NONE, the auc_3g entry is deleted.
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200211 * \param[in,out] dbc database context.
212 * \param[in] subscr_id DB ID of the subscriber.
213 * \param[in] aud Pointer to new auth data (in ASCII string form).
214 * \returns 0 on success, -EINVAL for invalid aud, -ENOENT for unknown
215 * subscr_id, -EIO for database errors.
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200216 */
217int db_subscr_update_aud_by_id(struct db_context *dbc, int64_t subscr_id,
218 const struct sub_auth_data_str *aud)
219{
220 sqlite3_stmt *stmt_del;
221 sqlite3_stmt *stmt_ins;
222 sqlite3_stmt *stmt;
223 const char *label;
224 int rc;
225 int ret = 0;
226
227 switch (aud->type) {
228 case OSMO_AUTH_TYPE_GSM:
229 label = "auc_2g";
230 stmt_del = dbc->stmt[DB_STMT_AUC_2G_DELETE];
231 stmt_ins = dbc->stmt[DB_STMT_AUC_2G_INSERT];
232
233 switch (aud->algo) {
234 case OSMO_AUTH_ALG_NONE:
235 case OSMO_AUTH_ALG_COMP128v1:
236 case OSMO_AUTH_ALG_COMP128v2:
237 case OSMO_AUTH_ALG_COMP128v3:
238 case OSMO_AUTH_ALG_XOR:
239 break;
240 case OSMO_AUTH_ALG_MILENAGE:
241 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
242 " auth algo not suited for 2G: %s\n",
243 osmo_auth_alg_name(aud->algo));
244 return -EINVAL;
245 default:
246 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
247 " Unknown auth algo: %d\n", aud->algo);
248 return -EINVAL;
249 }
250
251 if (aud->algo == OSMO_AUTH_ALG_NONE)
252 break;
253 if (!osmo_is_hexstr(aud->u.gsm.ki, 32, 32, true)) {
254 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
255 " Invalid KI: '%s'\n", aud->u.gsm.ki);
256 return -EINVAL;
257 }
258 break;
259
260 case OSMO_AUTH_TYPE_UMTS:
261 label = "auc_3g";
262 stmt_del = dbc->stmt[DB_STMT_AUC_3G_DELETE];
263 stmt_ins = dbc->stmt[DB_STMT_AUC_3G_INSERT];
264 switch (aud->algo) {
265 case OSMO_AUTH_ALG_NONE:
266 case OSMO_AUTH_ALG_MILENAGE:
267 break;
268 case OSMO_AUTH_ALG_COMP128v1:
269 case OSMO_AUTH_ALG_COMP128v2:
270 case OSMO_AUTH_ALG_COMP128v3:
271 case OSMO_AUTH_ALG_XOR:
272 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
273 " auth algo not suited for 3G: %s\n",
274 osmo_auth_alg_name(aud->algo));
275 return -EINVAL;
276 default:
277 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
278 " Unknown auth algo: %d\n", aud->algo);
279 return -EINVAL;
280 }
281
282 if (aud->algo == OSMO_AUTH_ALG_NONE)
283 break;
284 if (!osmo_is_hexstr(aud->u.umts.k, 32, 32, true)) {
285 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
286 " Invalid K: '%s'\n", aud->u.umts.k);
287 return -EINVAL;
288 }
289 if (!osmo_is_hexstr(aud->u.umts.opc, 32, 32, true)) {
290 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
291 " Invalid OP/OPC: '%s'\n", aud->u.umts.opc);
292 return -EINVAL;
293 }
294 if (aud->u.umts.ind_bitlen > OSMO_MILENAGE_IND_BITLEN_MAX) {
295 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
296 " Invalid ind_bitlen: %d\n", aud->u.umts.ind_bitlen);
297 return -EINVAL;
298 }
299 break;
300 default:
301 LOGP(DAUC, LOGL_ERROR, "Cannot update auth tokens:"
302 " unknown auth type: %d\n", aud->type);
303 return -EINVAL;
304 }
305
306 stmt = stmt_del;
307
308 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
309 return -EIO;
310
311 /* execute the statement */
312 rc = sqlite3_step(stmt);
313 if (rc != SQLITE_DONE) {
314 LOGP(DAUC, LOGL_ERROR,
315 "Cannot delete %s row: SQL error: (%d) %s\n",
316 label, rc, sqlite3_errmsg(dbc->db));
317 ret = -EIO;
318 goto out;
319 }
320
321 /* verify execution result */
322 rc = sqlite3_changes(dbc->db);
323 if (!rc)
324 /* Leave "no such entry" logging to the caller -- during
325 * db_subscr_delete_by_id(), we call this to make sure it is
326 * empty, and no entry is not an error then.*/
327 ret = -ENOENT;
328 else if (rc != 1) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100329 LOGP(DAUC, LOGL_ERROR, "Delete subscriber ID=%" PRId64
Neels Hofmeyr1332a172017-10-10 02:25:00 +0200330 " from %s: SQL modified %d rows (expected 1)\n",
331 subscr_id, label, rc);
332 ret = -EIO;
333 }
334
335 db_remove_reset(stmt);
336
337 /* Error situation? Return now. */
338 if (ret && ret != -ENOENT)
339 return ret;
340
341 /* Just delete requested? */
342 if (aud->algo == OSMO_AUTH_ALG_NONE)
343 return ret;
344
345 /* Don't return -ENOENT if inserting new data. */
346 ret = 0;
347
348 /* Insert new row */
349 stmt = stmt_ins;
350
351 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
352 return -EIO;
353
354 switch (aud->type) {
355 case OSMO_AUTH_TYPE_GSM:
356 if (!db_bind_int(stmt, "$algo_id_2g", aud->algo))
357 return -EIO;
358 if (!db_bind_text(stmt, "$ki", aud->u.gsm.ki))
359 return -EIO;
360 break;
361 case OSMO_AUTH_TYPE_UMTS:
362 if (!db_bind_int(stmt, "$algo_id_3g", aud->algo))
363 return -EIO;
364 if (!db_bind_text(stmt, "$k", aud->u.umts.k))
365 return -EIO;
366 if (!db_bind_text(stmt, "$op",
367 aud->u.umts.opc_is_op ? aud->u.umts.opc : NULL))
368 return -EIO;
369 if (!db_bind_text(stmt, "$opc",
370 aud->u.umts.opc_is_op ? NULL : aud->u.umts.opc))
371 return -EIO;
372 if (!db_bind_int(stmt, "$ind_bitlen", aud->u.umts.ind_bitlen))
373 return -EIO;
374 break;
375 default:
376 OSMO_ASSERT(false);
377 }
378
379 /* execute the statement */
380 rc = sqlite3_step(stmt);
381 if (rc != SQLITE_DONE) {
382 LOGP(DAUC, LOGL_ERROR,
383 "Cannot insert %s row: SQL error: (%d) %s\n",
384 label, rc, sqlite3_errmsg(dbc->db));
385 ret = -EIO;
386 goto out;
387 }
388
389out:
390 db_remove_reset(stmt);
391 return ret;
392}
393
Oliver Smith81db3892019-01-09 12:03:51 +0100394/*! Set a subscriber's IMEI in the HLR database.
395 * \param[in,out] dbc database context.
396 * \param[in] imsi ASCII string of IMSI digits
397 * \param[in] imei ASCII string of identifier digits, or NULL to remove the IMEI.
398 * \returns 0 on success, -ENOENT when the given subscriber does not exist,
399 * -EIO on database errors.
400 */
401int db_subscr_update_imei_by_imsi(struct db_context *dbc, const char* imsi, const char *imei)
402{
403 int rc, ret = 0;
404 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_IMEI_BY_IMSI];
405
406 if (imei && !osmo_imei_str_valid(imei, false)) {
407 LOGP(DAUC, LOGL_ERROR, "Cannot update subscriber IMSI='%s': invalid IMEI: '%s'\n", imsi, imei);
408 return -EINVAL;
409 }
410
411 if (!db_bind_text(stmt, "$imsi", imsi))
412 return -EIO;
413 if (imei && !db_bind_text(stmt, "$imei", imei))
414 return -EIO;
415
416 /* execute the statement */
417 rc = sqlite3_step(stmt);
418 if (rc != SQLITE_DONE) {
419 LOGP(DAUC, LOGL_ERROR, "Update IMEI for subscriber IMSI='%s': SQL Error: %s\n", imsi,
420 sqlite3_errmsg(dbc->db));
421 ret = -EIO;
422 goto out;
423 }
424
425 /* verify execution result */
426 rc = sqlite3_changes(dbc->db);
427 if (!rc) {
428 LOGP(DAUC, LOGL_ERROR, "Cannot update IMEI for subscriber IMSI='%s': no such subscriber\n", imsi);
429 ret = -ENOENT;
430 } else if (rc != 1) {
431 LOGP(DAUC, LOGL_ERROR, "Update IMEI for subscriber IMSI='%s': SQL modified %d rows (expected 1)\n",
432 imsi, rc);
433 ret = -EIO;
434 }
435
436out:
437 db_remove_reset(stmt);
438 return ret;
439}
440
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200441/* Common code for db_subscr_get_by_*() functions. */
442static int db_sel(struct db_context *dbc, sqlite3_stmt *stmt, struct hlr_subscriber *subscr,
443 const char **err)
Harald Weltee687be52016-05-03 18:49:27 +0200444{
Maxadc66482017-02-20 11:23:20 +0100445 int rc;
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200446 int ret = 0;
Stefan Sperling5c14c9c2018-12-07 12:30:21 +0100447 const char *last_lu_seen_str;
Oliver Smith63de00c2019-06-04 12:18:53 +0200448 struct tm tm = {0};
Harald Weltee687be52016-05-03 18:49:27 +0200449
450 /* execute the statement */
451 rc = sqlite3_step(stmt);
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200452 if (rc == SQLITE_DONE) {
453 ret = -ENOENT;
454 goto out;
455 }
Harald Weltee687be52016-05-03 18:49:27 +0200456 if (rc != SQLITE_ROW) {
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200457 ret = -EIO;
458 goto out;
Maxadc66482017-02-20 11:23:20 +0100459 }
460
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200461 if (!subscr)
462 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200463
Neels Hofmeyrf1949f72018-12-26 01:49:53 +0100464 *subscr = hlr_subscriber_empty;
Neels Hofmeyrb6837e32017-10-10 23:20:26 +0200465
Harald Weltee687be52016-05-03 18:49:27 +0200466 /* obtain the various columns */
467 subscr->id = sqlite3_column_int64(stmt, 0);
Neels Hofmeyrdbced932017-10-27 02:57:51 +0200468 copy_sqlite3_text_to_buf(subscr->imsi, stmt, 1);
469 copy_sqlite3_text_to_buf(subscr->msisdn, stmt, 2);
Oliver Smith81db3892019-01-09 12:03:51 +0100470 copy_sqlite3_text_to_buf(subscr->imei, stmt, 3);
Harald Welte99909272016-05-05 18:24:15 +0200471 /* FIXME: These should all be BLOBs as they might contain NUL */
Oliver Smith81db3892019-01-09 12:03:51 +0100472 copy_sqlite3_text_to_buf(subscr->vlr_number, stmt, 4);
473 copy_sqlite3_text_to_buf(subscr->sgsn_number, stmt, 5);
474 copy_sqlite3_text_to_buf(subscr->sgsn_address, stmt, 6);
475 subscr->periodic_lu_timer = sqlite3_column_int(stmt, 7);
476 subscr->periodic_rau_tau_timer = sqlite3_column_int(stmt, 8);
477 subscr->nam_cs = sqlite3_column_int(stmt, 9);
478 subscr->nam_ps = sqlite3_column_int(stmt, 10);
479 subscr->lmsi = sqlite3_column_int(stmt, 11);
480 subscr->ms_purged_cs = sqlite3_column_int(stmt, 12);
481 subscr->ms_purged_ps = sqlite3_column_int(stmt, 13);
482 last_lu_seen_str = (const char *)sqlite3_column_text(stmt, 14);
Stefan Sperling5c14c9c2018-12-07 12:30:21 +0100483 if (last_lu_seen_str && last_lu_seen_str[0] != '\0') {
484 if (strptime(last_lu_seen_str, DB_LAST_LU_SEEN_FMT, &tm) == NULL) {
485 LOGP(DAUC, LOGL_ERROR, "Cannot parse last LU timestamp '%s' of subscriber with IMSI='%s': %s\n",
486 last_lu_seen_str, subscr->imsi, strerror(errno));
487 } else {
488 subscr->last_lu_seen = mktime(&tm);
489 if (subscr->last_lu_seen == -1) {
490 LOGP(DAUC, LOGL_ERROR, "Cannot convert LU timestamp '%s' to time_t: %s\n",
491 last_lu_seen_str, strerror(errno));
492 subscr->last_lu_seen = 0;
493 }
494 }
495 }
Neels Hofmeyrb984a6c2018-12-29 03:28:38 +0100496 copy_sqlite3_text_to_buf(subscr->last_lu_rat, stmt, 15);
Harald Weltee687be52016-05-03 18:49:27 +0200497
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200498out:
Max00b37152017-02-20 11:09:27 +0100499 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200500
Neels Hofmeyrf1949f72018-12-26 01:49:53 +0100501 if (ret == 0)
502 db_subscr_get_rat_types(dbc, subscr);
503
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200504 switch (ret) {
505 case 0:
506 *err = NULL;
507 break;
508 case -ENOENT:
509 *err = "No such subscriber";
510 break;
511 default:
512 *err = sqlite3_errmsg(dbc->db);
513 break;
514 }
515 return ret;
516}
517
Oliver Smith6b73fd92019-03-06 13:49:05 +0100518/*! Check if a subscriber exists in the HLR database.
519 * \param[in, out] dbc database context.
520 * \param[in] imsi ASCII string of IMSI digits.
521 * \returns 0 if it exists, -ENOENT if it does not exist, -EIO on database error.
522 */
523int db_subscr_exists_by_imsi(struct db_context *dbc, const char *imsi) {
524 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_EXISTS_BY_IMSI];
525 const char *err;
526 int rc;
527
528 if (!db_bind_text(stmt, NULL, imsi))
529 return -EIO;
530
531 rc = sqlite3_step(stmt);
532 db_remove_reset(stmt);
533 if (rc == SQLITE_ROW)
534 return 0; /* exists */
535 if (rc == SQLITE_DONE)
536 return -ENOENT; /* does not exist */
537
538 err = sqlite3_errmsg(dbc->db);
539 LOGP(DAUC, LOGL_ERROR, "Failed to check if subscriber exists by IMSI='%s': %s\n", imsi, err);
540 return rc;
541}
542
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200543/*! Retrieve subscriber data from the HLR database.
544 * \param[in,out] dbc database context.
545 * \param[in] imsi ASCII string of IMSI digits.
546 * \param[out] subscr place retrieved data in this struct.
547 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
548 * database error.
549 */
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200550int db_subscr_get_by_imsi(struct db_context *dbc, const char *imsi,
551 struct hlr_subscriber *subscr)
552{
553 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_IMSI];
554 const char *err;
555 int rc;
556
557 if (!db_bind_text(stmt, NULL, imsi))
558 return -EIO;
559
560 rc = db_sel(dbc, stmt, subscr, &err);
561 if (rc)
562 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMSI='%s': %s\n",
563 imsi, err);
564 return rc;
565}
566
Vadim Yanitskiyc13599d2019-03-30 17:03:42 +0700567/*! Check if a subscriber exists in the HLR database.
568 * \param[in, out] dbc database context.
569 * \param[in] msisdn ASCII string of MSISDN digits.
570 * \returns 0 if it exists, -ENOENT if it does not exist, -EIO on database error.
571 */
572int db_subscr_exists_by_msisdn(struct db_context *dbc, const char *msisdn)
573{
574 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_EXISTS_BY_MSISDN];
575 const char *err;
576 int rc;
577
578 if (!db_bind_text(stmt, NULL, msisdn))
579 return -EIO;
580
581 rc = sqlite3_step(stmt);
582 db_remove_reset(stmt);
583 if (rc == SQLITE_ROW)
584 return 0; /* exists */
585 if (rc == SQLITE_DONE)
586 return -ENOENT; /* does not exist */
587
588 err = sqlite3_errmsg(dbc->db);
589 LOGP(DAUC, LOGL_ERROR, "Failed to check if subscriber exists "
590 "by MSISDN='%s': %s\n", msisdn, err);
591 return rc;
592}
593
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200594/*! Retrieve subscriber data from the HLR database.
595 * \param[in,out] dbc database context.
596 * \param[in] msisdn ASCII string of MSISDN digits.
597 * \param[out] subscr place retrieved data in this struct.
598 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
599 * database error.
600 */
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200601int db_subscr_get_by_msisdn(struct db_context *dbc, const char *msisdn,
602 struct hlr_subscriber *subscr)
603{
604 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_MSISDN];
605 const char *err;
606 int rc;
607
608 if (!db_bind_text(stmt, NULL, msisdn))
609 return -EIO;
610
611 rc = db_sel(dbc, stmt, subscr, &err);
612 if (rc)
613 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: MSISDN='%s': %s\n",
614 msisdn, err);
615 return rc;
616}
617
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200618/*! Retrieve subscriber data from the HLR database.
619 * \param[in,out] dbc database context.
620 * \param[in] id ID of the subscriber in the HLR db.
621 * \param[out] subscr place retrieved data in this struct.
622 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
623 * database error.
624 */
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200625int db_subscr_get_by_id(struct db_context *dbc, int64_t id,
626 struct hlr_subscriber *subscr)
627{
628 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_ID];
629 const char *err;
630 int rc;
631
632 if (!db_bind_int64(stmt, NULL, id))
633 return -EIO;
634
635 rc = db_sel(dbc, stmt, subscr, &err);
636 if (rc)
Stefan Sperling705b61b2018-12-07 12:44:50 +0100637 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: ID=%" PRId64 ": %s\n",
Neels Hofmeyr9c2bbc82017-10-09 17:30:32 +0200638 id, err);
639 return rc;
Harald Weltee687be52016-05-03 18:49:27 +0200640}
641
Oliver Smith81db3892019-01-09 12:03:51 +0100642/*! Retrieve subscriber data from the HLR database.
643 * \param[in,out] dbc database context.
644 * \param[in] imei ASCII string of identifier digits
645 * \param[out] subscr place retrieved data in this struct.
646 * \returns 0 on success, -ENOENT if no such subscriber was found, -EIO on
647 * database error.
648 */
649int db_subscr_get_by_imei(struct db_context *dbc, const char *imei, struct hlr_subscriber *subscr)
650{
651 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_SEL_BY_IMEI];
652 const char *err;
653 int rc;
654
655 if (!db_bind_text(stmt, NULL, imei))
656 return -EIO;
657
658 rc = db_sel(dbc, stmt, subscr, &err);
659 if (rc)
660 LOGP(DAUC, LOGL_ERROR, "Cannot read subscriber from db: IMEI=%s: %s\n", imei, err);
661 return rc;
662}
663
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200664/*! You should use hlr_subscr_nam() instead; enable or disable PS or CS for a
665 * subscriber without notifying GSUP clients.
666 * \param[in,out] dbc database context.
667 * \param[in] imsi ASCII string of IMSI digits.
668 * \param[in] nam_val True to enable CS/PS, false to disable.
669 * \param[in] is_ps when true, set nam_ps, else set nam_cs.
670 * \returns 0 on success, -ENOENT when the given IMSI does not exist, -EIO on
671 * database errors.
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200672 */
673int db_subscr_nam(struct db_context *dbc, const char *imsi, bool nam_val, bool is_ps)
Max3ce36862017-02-20 11:18:04 +0100674{
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200675 sqlite3_stmt *stmt;
Max3ce36862017-02-20 11:18:04 +0100676 int rc;
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200677 int ret = 0;
Max3ce36862017-02-20 11:18:04 +0100678
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200679 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_NAM_PS_BY_IMSI
680 : DB_STMT_UPD_NAM_CS_BY_IMSI];
Max3ce36862017-02-20 11:18:04 +0100681
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200682 if (!db_bind_text(stmt, "$imsi", imsi))
683 return -EIO;
684 if (!db_bind_int(stmt, "$val", nam_val ? 1 : 0))
685 return -EIO;
686
687 /* execute the statement */
688 rc = sqlite3_step(stmt);
Max3ce36862017-02-20 11:18:04 +0100689 if (rc != SQLITE_DONE) {
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200690 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL error: %s\n",
691 nam_val ? "enable" : "disable",
692 is_ps ? "PS" : "CS",
693 sqlite3_errmsg(dbc->db));
694 ret = -EIO;
695 goto out;
Max3ce36862017-02-20 11:18:04 +0100696 }
697
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200698 /* verify execution result */
699 rc = sqlite3_changes(dbc->db);
700 if (!rc) {
701 LOGP(DAUC, LOGL_ERROR, "Cannot %s %s: no such subscriber: IMSI='%s'\n",
702 nam_val ? "enable" : "disable",
703 is_ps ? "PS" : "CS",
704 imsi);
705 ret = -ENOENT;
706 goto out;
707 } else if (rc != 1) {
708 LOGHLR(imsi, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
709 nam_val ? "enable" : "disable",
710 is_ps ? "PS" : "CS",
Max3ce36862017-02-20 11:18:04 +0100711 rc);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200712 ret = -EIO;
Max3ce36862017-02-20 11:18:04 +0100713 }
714
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200715out:
Max3ce36862017-02-20 11:18:04 +0100716 db_remove_reset(stmt);
Neels Hofmeyre8ccd502017-10-06 04:10:06 +0200717 return ret;
Max3ce36862017-02-20 11:18:04 +0100718}
719
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200720/*! Record a Location Updating in the database.
721 * \param[in,out] dbc database context.
722 * \param[in] subscr_id ID of the subscriber in the HLR db.
723 * \param[in] vlr_or_sgsn_number ASCII string of identifier digits.
724 * \param[in] is_ps when true, set sgsn_number, else set vlr_number.
725 * \returns 0 on success, -ENOENT when the given subscriber does not exist,
726 * -EIO on database errors.
727 */
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200728int db_subscr_lu(struct db_context *dbc, int64_t subscr_id,
Neels Hofmeyrb984a6c2018-12-29 03:28:38 +0100729 const char *vlr_or_sgsn_number, bool is_ps,
730 const enum osmo_rat_type rat_types[], size_t rat_types_len)
Harald Weltee687be52016-05-03 18:49:27 +0200731{
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200732 sqlite3_stmt *stmt;
Harald Weltee687be52016-05-03 18:49:27 +0200733 int rc, ret = 0;
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100734 struct timespec localtime;
Neels Hofmeyrb984a6c2018-12-29 03:28:38 +0100735 char rat_types_str[128] = "";
736 int i;
Harald Weltee687be52016-05-03 18:49:27 +0200737
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200738 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_SGSN_BY_ID
739 : DB_STMT_UPD_VLR_BY_ID];
Harald Weltee687be52016-05-03 18:49:27 +0200740
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200741 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
742 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200743
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200744 if (!db_bind_text(stmt, "$number", vlr_or_sgsn_number))
745 return -EIO;
Harald Weltee687be52016-05-03 18:49:27 +0200746
747 /* execute the statement */
748 rc = sqlite3_step(stmt);
749 if (rc != SQLITE_DONE) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100750 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%" PRId64 ": SQL Error: %s\n",
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200751 is_ps? "SGSN" : "VLR", subscr_id, sqlite3_errmsg(dbc->db));
752 ret = -EIO;
753 goto out;
Harald Weltee687be52016-05-03 18:49:27 +0200754 }
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200755
756 /* verify execution result */
757 rc = sqlite3_changes(dbc->db);
758 if (!rc) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100759 LOGP(DAUC, LOGL_ERROR, "Cannot update %s number for subscriber ID=%" PRId64
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200760 ": no such subscriber\n",
761 is_ps? "SGSN" : "VLR", subscr_id);
762 ret = -ENOENT;
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100763 goto out;
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200764 } else if (rc != 1) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100765 LOGP(DAUC, LOGL_ERROR, "Update %s number for subscriber ID=%" PRId64
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200766 ": SQL modified %d rows (expected 1)\n",
767 is_ps? "SGSN" : "VLR", subscr_id, rc);
768 ret = -EIO;
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100769 goto out;
Neels Hofmeyrdd783052017-10-09 17:36:08 +0200770 }
771
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100772 db_remove_reset(stmt);
773
774 if (osmo_clock_gettime(CLOCK_REALTIME, &localtime) != 0) {
775 LOGP(DAUC, LOGL_ERROR, "Cannot get the current time: (%d) %s\n", errno, strerror(errno));
776 ret = -errno;
777 goto out;
778 }
779
780 stmt = dbc->stmt[DB_STMT_SET_LAST_LU_SEEN];
781
782 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
783 return -EIO;
784 /* The timestamp will be converted to UTC by SQLite. */
785 if (!db_bind_int64(stmt, "$val", (int64_t)localtime.tv_sec)) {
786 ret = -EIO;
787 goto out;
788 }
789
Neels Hofmeyrb984a6c2018-12-29 03:28:38 +0100790 for (i = 0; i < rat_types_len; i++) {
791 char *pos = rat_types_str + strnlen(rat_types_str, sizeof(rat_types_str));
Neels Hofmeyrd2d9f352018-12-29 04:10:35 +0100792 int len = sizeof(rat_types_str) - (pos - rat_types_str);
Neels Hofmeyrb984a6c2018-12-29 03:28:38 +0100793 rc = snprintf(pos, len, "%s%s", pos == rat_types_str ? "" : ",", osmo_rat_type_name(rat_types[i]));
794 if (rc > len) {
795 osmo_strlcpy(rat_types_str + sizeof(rat_types_str) - 4, "...", 4);
796 break;
797 }
798 }
799
800 if (!db_bind_text(stmt, "$rat", rat_types_str)) {
801 ret = -EIO;
802 goto out;
803 }
804
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100805 rc = sqlite3_step(stmt);
806 if (rc != SQLITE_DONE) {
807 LOGP(DAUC, LOGL_ERROR,
Stefan Sperling705b61b2018-12-07 12:44:50 +0100808 "Cannot update LU timestamp for subscriber ID=%" PRId64 ": SQL error: (%d) %s\n",
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100809 subscr_id, rc, sqlite3_errmsg(dbc->db));
810 ret = -EIO;
811 goto out;
812 }
813
814 /* verify execution result */
815 rc = sqlite3_changes(dbc->db);
816 if (!rc) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100817 LOGP(DAUC, LOGL_ERROR, "Cannot update LU timestamp for subscriber ID=%" PRId64
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100818 ": no such subscriber\n", subscr_id);
819 ret = -ENOENT;
820 goto out;
821 } else if (rc != 1) {
Stefan Sperling705b61b2018-12-07 12:44:50 +0100822 LOGP(DAUC, LOGL_ERROR, "Update LU timestamp for subscriber ID=%" PRId64
Stefan Sperling638ba8c2018-12-04 15:07:29 +0100823 ": SQL modified %d rows (expected 1)\n", subscr_id, rc);
824 ret = -EIO;
825 }
Harald Weltee687be52016-05-03 18:49:27 +0200826out:
Max00b37152017-02-20 11:09:27 +0100827 db_remove_reset(stmt);
Harald Weltee687be52016-05-03 18:49:27 +0200828 return ret;
829}
Harald Welteb18f0e02016-05-05 21:03:03 +0200830
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200831/*! Set the ms_purged_cs or ms_purged_ps values in the database.
832 * \param[in,out] dbc database context.
833 * \param[in] by_imsi ASCII string of IMSI digits.
834 * \param[in] purge_val true to purge, false to un-purge.
835 * \param[in] is_ps when true, set ms_purged_ps, else set ms_purged_cs.
836 * \returns 0 on success, -ENOENT when the given IMSI does not exist, -EIO on
837 * database errors.
838 */
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200839int db_subscr_purge(struct db_context *dbc, const char *by_imsi,
840 bool purge_val, bool is_ps)
Harald Welteb18f0e02016-05-05 21:03:03 +0200841{
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200842 sqlite3_stmt *stmt;
843 int rc, ret = 0;
Harald Welteb18f0e02016-05-05 21:03:03 +0200844
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200845 stmt = dbc->stmt[is_ps ? DB_STMT_UPD_PURGE_PS_BY_IMSI
846 : DB_STMT_UPD_PURGE_CS_BY_IMSI];
Harald Welteb18f0e02016-05-05 21:03:03 +0200847
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200848 if (!db_bind_text(stmt, "$imsi", by_imsi))
849 return -EIO;
850 if (!db_bind_int(stmt, "$val", purge_val ? 1 : 0))
851 return -EIO;
Harald Welteb18f0e02016-05-05 21:03:03 +0200852
853 /* execute the statement */
854 rc = sqlite3_step(stmt);
855 if (rc != SQLITE_DONE) {
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200856 LOGP(DAUC, LOGL_ERROR, "%s %s: SQL error: %s\n",
857 purge_val ? "purge" : "un-purge",
858 is_ps ? "PS" : "CS",
859 sqlite3_errmsg(dbc->db));
860 ret = -EIO;
861 goto out;
Harald Welteb18f0e02016-05-05 21:03:03 +0200862 }
Max00b37152017-02-20 11:09:27 +0100863
Neels Hofmeyre50121e2017-10-09 17:48:51 +0200864 /* verify execution result */
865 rc = sqlite3_changes(dbc->db);
866 if (!rc) {
867 LOGP(DAUC, LOGL_ERROR, "Cannot %s %s: no such subscriber: IMSI='%s'\n",
868 purge_val ? "purge" : "un-purge",
869 is_ps ? "PS" : "CS",
870 by_imsi);
871 ret = -ENOENT;
872 goto out;
873 } else if (rc != 1) {
874 LOGHLR(by_imsi, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
875 purge_val ? "purge" : "un-purge",
876 is_ps ? "PS" : "CS",
877 rc);
878 ret = -EIO;
879 }
880
881out:
Max00b37152017-02-20 11:09:27 +0100882 db_remove_reset(stmt);
Harald Welteb18f0e02016-05-05 21:03:03 +0200883
884 return ret;
885}
Neels Hofmeyr00b1d432017-10-17 01:43:48 +0200886
887/*! Update nam_cs/nam_ps in the db and trigger notifications to GSUP clients.
Neels Hofmeyr16140f72017-10-25 19:17:18 +0200888 * \param[in,out] hlr Global hlr context.
889 * \param[in] subscr Subscriber from a fresh db_subscr_get_by_*() call.
890 * \param[in] nam_val True to enable CS/PS, false to disable.
891 * \param[in] is_ps True to enable/disable PS, false for CS.
Neels Hofmeyr00b1d432017-10-17 01:43:48 +0200892 * \returns 0 on success, ENOEXEC if there is no need to change, a negative
893 * value on error.
894 */
895int hlr_subscr_nam(struct hlr *hlr, struct hlr_subscriber *subscr, bool nam_val, bool is_ps)
896{
897 int rc;
898 struct lu_operation *luop;
899 struct osmo_gsup_conn *co;
900 bool is_val = is_ps? subscr->nam_ps : subscr->nam_cs;
901
902 if (is_val == nam_val) {
903 LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
904 nam_val ? "enable" : "disable", is_ps ? "PS" : "CS");
905 return ENOEXEC;
906 }
907
908 rc = db_subscr_nam(hlr->dbc, subscr->imsi, nam_val, is_ps);
909 if (rc)
910 return rc > 0? -rc : rc;
911
912 /* If we're disabling, send a notice out to the GSUP client that is
913 * responsible. Otherwise no need. */
914 if (nam_val)
915 return 0;
916
917 /* FIXME: only send to single SGSN where latest update for IMSI came from */
918 llist_for_each_entry(co, &hlr->gs->clients, list) {
919 luop = lu_op_alloc_conn(co);
920 if (!luop) {
921 LOGHLR(subscr->imsi, LOGL_ERROR,
922 "Cannot notify GSUP client, cannot allocate lu_operation,"
923 " for %s:%u\n",
924 co && co->conn && co->conn->server? co->conn->server->addr : "unset",
925 co && co->conn && co->conn->server? co->conn->server->port : 0);
926 continue;
927 }
928 luop->subscr = *subscr;
929 lu_op_tx_del_subscr_data(luop);
930 lu_op_free(luop);
931 }
932 return 0;
933}
Neels Hofmeyrf1949f72018-12-26 01:49:53 +0100934
935int db_subscr_set_rat_type_flag(struct db_context *dbc, int64_t subscr_id, enum osmo_rat_type rat, bool allowed)
936{
937 int rc;
938 int ret = 0;
939 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_UPD_RAT_FLAG];
940
941 if (!db_bind_int64(stmt, "$subscriber_id", subscr_id))
942 return -EIO;
943
944 OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
945 if (!db_bind_text(stmt, "$rat", osmo_rat_type_name(rat)))
946 return -EIO;
947
948 if (!db_bind_int(stmt, "$allowed", allowed ? 1 : 0))
949 return -EIO;
950
951 /* execute the statement */
952 rc = sqlite3_step(stmt);
953 if (rc != SQLITE_DONE) {
954 LOGP(DDB, LOGL_ERROR, "%s %s: SQL error: %s\n",
955 allowed ? "enable" : "disable", osmo_rat_type_name(rat),
956 sqlite3_errmsg(dbc->db));
957 ret = -EIO;
958 goto out;
959 }
960
961 /* verify execution result */
962 rc = sqlite3_changes(dbc->db);
963 if (!rc) {
964 LOGP(DDB, LOGL_ERROR, "Cannot %s %s: no such subscriber: ID=%" PRIu64 "\n",
965 allowed ? "enable" : "disable", osmo_rat_type_name(rat),
966 subscr_id);
967 ret = -ENOENT;
968 goto out;
969 } else if (rc != 1) {
970 LOGP(DDB, LOGL_ERROR, "%s %s: SQL modified %d rows (expected 1)\n",
971 allowed ? "enable" : "disable", osmo_rat_type_name(rat),
972 rc);
973 ret = -EIO;
974 }
975
976out:
977 db_remove_reset(stmt);
978 return ret;
979}
980
981int db_subscr_get_rat_types(struct db_context *dbc, struct hlr_subscriber *subscr)
982{
983 int rc;
984 int ret = 0;
985 int i;
986 sqlite3_stmt *stmt = dbc->stmt[DB_STMT_RAT_BY_ID];
987
988 if (!db_bind_int64(stmt, "$subscriber_id", subscr->id))
989 return -EIO;
990
991 for (i = 0; i < OSMO_RAT_COUNT; i++)
992 subscr->rat_types[i] = true;
993
994 /* execute the statement */
995 while (1) {
996 enum osmo_rat_type rat;
997 bool allowed;
998
999 rc = sqlite3_step(stmt);
1000
1001 if (rc == SQLITE_DONE)
1002 break;
1003 if (rc != SQLITE_ROW)
1004 return -rc;
1005
1006 rc = get_string_value(osmo_rat_type_names, (const char*)sqlite3_column_text(stmt, 0));
1007 if (rc == -EINVAL) {
1008 ret = -EINVAL;
1009 goto out;
1010 }
1011 if (rc <= 0 || rc >= OSMO_RAT_COUNT) {
1012 ret = -EINVAL;
1013 goto out;
1014 }
1015 rat = rc;
1016
1017 allowed = sqlite3_column_int(stmt, 1);
1018
1019 subscr->rat_types[rat] = allowed;
1020 LOGP(DAUC, LOGL_DEBUG, "db: imsi='%s' %s %s\n",
1021 subscr->imsi, osmo_rat_type_name(rat), allowed ? "allowed" : "forbidden");
1022 }
1023
1024out:
1025 db_remove_reset(stmt);
1026 return ret;
1027}
1028
1029int hlr_subscr_rat_flag(struct hlr *hlr, struct hlr_subscriber *subscr, enum osmo_rat_type rat, bool allowed)
1030{
1031 int rc;
1032 OSMO_ASSERT(rat >= 0 && rat < OSMO_RAT_COUNT);
1033
1034 db_subscr_get_rat_types(hlr->dbc, subscr);
1035
1036 if (subscr->rat_types[rat] == allowed) {
1037 LOGHLR(subscr->imsi, LOGL_DEBUG, "Already has the requested value when asked to %s %s\n",
1038 allowed ? "enable" : "disable", osmo_rat_type_name(rat));
1039 return -ENOEXEC;
1040 }
1041
1042 rc = db_subscr_set_rat_type_flag(hlr->dbc, subscr->id, rat, allowed);
1043 if (rc)
1044 return rc > 0? -rc : rc;
1045
1046 /* FIXME: If we're disabling, send message to VLR to detach subscriber */
1047
1048 return 0;
1049}